﻿
if (typeof jQuery === 'undefined') {
    throw new Error('jquery.form JavaScript requires jQuery');
}

if (typeof Plugin === 'undefined') {
    throw new Error('jquery.form JavaScript requires Plugin');
}


(function ($) {
    'use strict';

    var Form = function (el, options) {
        this.$el = $(el);
        this.namespace = _config_.form.namespace;
        this.defaults = _config_.form.defaults;
        this.methods = _config_.form.methods;
        this.events = _config_.form.events;

        this.initials = $.com.getDataBindFromElement(this.$el);
        this.initials = $.extend(true, {}, _config_.form.initials, this.initials);
        //刷新或重新获取属于 form 的 input 和子 form 及父级 form
        this.refreshForm();
        this.originals = $.extend(true, {}, this.defaults, this.initials);
        this.options = $.extend(true, {}, this.originals, options);
        if ($.com.hasValue(options)) {
            this.options.data = $.extend(true, options.data, this.options.data);
        }
        this.options.serial = $.com.getSerial(16);
        this.isEditMode();

        //处理id
        this.getNamedPrefix();
        this.$el.data(_config_.form.namespace, this);
        this.init();
    };

    Form.prototype = new Plugin();

    Form.prototype.init = function () {
        this.initData();
    };

    Form.prototype.reInit = function () {
        if ($.com.isJqueryObject(this.$inputs)) {
            this.$inputs.input('reInit');
            this.$popups.popup('init');
            $(_config_.selector.tooltip).tooltip();
        }

        if ($.com.isJqueryObject(this.$buttons)) {
            this.$buttons.button();
        }

        if ($.com.isJqueryObject(this.$forms)) {
            this.$forms.form('reInit');
        }
    };

    Form.prototype.initForm = function () {
        this.execute(this.options.loading);
        //draw Form
        if (!this.$el.is(_config_.selector.formArray)) {
            this.options.data.serial = this.options.serial;
        }

        this.$el.attr(_config_.attribute.serial, this.options.serial);
        if ($.com.hasValue(this.options.data)) {
            var removable = this.options.data.removable || ((this.options.data.DataStatus || '').toLowerCase() === 'deleted' ? false : undefined);
            if ($.com.hasValue(removable)) {
                this.$el.attr('data-removable', removable);
            }
        }

        if ($.com.isJqueryObject(this.$buttons)) {
            this.$buttons.button();
        }

        if ($.com.isJqueryObject(this.$inputs)) {
            this.$inputs.input();
            this.$popups.popup();
            this.$el.find(_config_.selector.tooltip).tooltip();
        }

        this.refresh();
        this.readonly();
        this.display();
        this.authorizeButton();

        this.execute(this.options.loaded);
    };

    Form.prototype.initData = function () {
        this.execute(this.options.binding);

        if (this.options.side === 'server' && $.com.hasValue(this.options.url)) {
            var that = this;
            $.api.ajax(that.options.url, '', { serial: $.com.getSerial() }, that.options.httpMethod, function (data) {
                that.options.data = data;
                if ($.isFunction(that.options.dataAdapter)) {
                    that.options.data = that.options.dataAdapter(data) || {};
                    if ($.com.isArray(that.options.data) && that.options.type !== _config_.default.arrayTypeName) {
                        that.options.data = that.options.data[0];
                    }
                }

                if ($.com.isJqueryObject(that.$form)) {
                    var form = that.$form.data(_config_.form.namespace);
                    if ($.com.hasValue(form)) {
                        if (form.options.type === _config_.default.arrayTypeName) {
                            if (!$.com.isArray(form.options.data)) {
                                form.options.data = [];
                            }

                            form.options.data.push(that.options.data);
                        } else {
                            form.options.data[that.options.field] = that.options.data;
                        }
                    }
                }

                that.isEditMode();
                that.execute(that.options.bound);
                that.initForm();
            });
        } else {
            this.execute(this.options.bound);
            this.initForm();
        }
    };

    Form.prototype.readonly = function (arg) {
        this.options.readonly = $.com.hasValue(arg) ? arg : this.options.readonly;

        if ($.com.isJqueryObject(this.$inputs)) {
            this.$inputs.input('readonly', this.options.readonly);
        }

        if ($.com.isJqueryObject(this.$forms)) {
            this.$forms.form('readonly', this.options.readonly);
        }
    };

    Form.prototype.display = function (arg) {
        this.options.display = $.com.hasValue(arg) ? arg : this.options.display;
        if ($.com.hasValue(this.options.display) && !!this.options.display) {
            this.$el.show();
        } else {
            this.$el.hide();
        }
    };

    Form.prototype.refreshForm = function () {
        var that = this;
        //子级input组件
        this.$inputs = $(_config_.selector.default);
        //子级form组件
        this.$forms = $(_config_.selector.default);
        //子级form组件
        this.$buttons = $(_config_.selector.default);
        //父级form组件
        this.$form = this.$el.attr(_config_.attribute.formAttr);
        if ($.com.hasValue(this.$form)) {
            this.$form = $(this.$form).first();
        } else {
            this.$form = this.$el.parents(_config_.selector.form).first();
        }

        var fields = this.$el.find(_config_.selector.input).not(_config_.selector.form);
        if ($.com.isJqueryObject(fields)) {
            fields.each(function () {
                if ($(this).isBelongToForm(that.$el)) {
                    that.$inputs.push(this);
                }
            });
        }

        var buttons = this.$el.find(_config_.selector.button);
        if ($.com.isJqueryObject(buttons)) {
            buttons.each(function () {
                var $this = $(this);
                if ($this.isBelongToForm(that.$el)) {
                    that.$buttons.push(this);
                }
            });
        }

        var forms = this.$el.find(_config_.selector.form);
        if ($.com.isJqueryObject(forms)) {
            forms.each(function () {
                var $this = $(this);
                if ($this.isBelongToForm(that.$el)) {
                    that.$forms.push(this);
                }
            });
        }

        //this.$validator = undefined;
        //父级弹出框
        this.$popup = this.$el.parents(_config_.selector.popupWindow);
        //子级弹出框启动器
        this.$popups = this.$inputs.filter(_config_.selector.popup);
        this.$tooltips = this.$inputs.filter(_config_.selector.tooltip);

        //grid提供给form子组件使用, 这样子就不需要给每个需要使用grid的组件绑定grid
        if (this.$popup.hasValue()) {
            this.$grid = this.$popup.find(this.initials.grid);
        } else if (this.$form.hasValue()) {
            this.$grid = this.$form.find(this.initials.grid);
        } else {
            this.$grid = $(this.initials.grid || _config_.selector.default);
        }
        if (this.$grid.hasValue() && this.$grid.length > 1) {
            this.$grid = this.$grid.first();
        }

        if ($.com.hasValue(this.initials.template)) {
            this.$template = $(this.initials.template).children();
        }
    };

    Form.prototype.refreshLayout = function () {
        var $heading = this.$el.prev();
        if ($heading.is('.form-array-heading') || $heading.is('.form-heading')) {
            if (this.$forms.hasValue()) {
                this.$el.prev().removeClass('form-heading').addClass('form-array-heading');
            } else {
                this.$el.prev().removeClass('form-array-heading').addClass('form-heading');
            }
        }

        if ($.com.isJqueryObject(this.$forms) && this.$forms.length <= 1) {
            var formData = this.$forms.eq(0).data(_config_.form.namespace);
            if ($.com.hasValue(formData) && $.com.isJqueryObject(formData.$buttons)) {
                formData.$buttons.filter('[data-button="removeForm"]').hide();
            }
        } else {
            this.$forms.each(function () {
                var $this = $(this);
                var formData = $this.data(_config_.form.namespace);
                if ($.com.hasValue(formData) && $.com.isJqueryObject(formData.$buttons)) {
                    formData.$buttons.filter('[data-button="removeForm"]').show();
                }
            });
        }
    };

    //重新设置组件状态
    Form.prototype.setStatus = function (arg) {
        if ($.com.isJqueryObject(this.$inputs)) {
            this.$inputs.input(arg);
        }

        if ($.com.isJqueryObject(this.$forms)) {
            this.$forms.form(arg);
        }
    };

    Form.prototype.clear = function () {
        this.setStatus('clear');
    };

    Form.prototype.reset = function () {
        this.setStatus('reset');
    };

    Form.prototype.isEditMode = function () {
        if (this.options.type === _config_.default.arrayTypeName) {
            this.options.isEdit = $.com.isArray(this.options.data);
        } else {
            if (this.options.side === 'server' && $.com.hasValue(this.options.url)) {
                this.options.isEdit = true;
            } else {
                var data = $.com.cleanData(this.options.data);
                this.options.isEdit = !$.com.isNullOrEmpty(data);
            }
        }

        return this.options.isEdit;
    };

    Form.prototype.refresh = function (data, force) {
        if (!$.com.isNullOrEmpty(data) || $.com.hasValue(force) && force) {
            this.options.data = data;
        }

        if (!$.isArray(this.options.data)) {
            this.options.data.serial = this.options.serial;
        }

        this.isEditMode();

        if ($.com.isJqueryObject(this.$inputs)) {
            this.$inputs.input('refresh', undefined, force);
        }

        var that = this;
        //初始化form, 分四种情况, 
        //1. 新增模式, 无数据, 有模板且为 array 且 autoDraw=true, 则画一个空的, 否则直接初始化
        //2. 新增模式, 无数据, 无模板且为 array 且 autoDraw=false, 此时 form 中应该已经存在子元素， 应该直接初始化
        //3. 编辑模式, 有数据, 有模版且为 array 则按数据画出来子 form;  有数据无模板且为 array, 而且子 form 存在, 则使用数据和子 form 初始化
        //4. 编辑模式, 有数据, 无模板且数据不是 array, 则用数据初始化子 form
        if (this.options.type === _config_.default.arrayTypeName) {
            //当前form为数组, 子级form需根据新增编辑区分
            if (!$.com.isArray(this.options.data)) {
                //当前form无数据, 则为新增
                if ($.com.isJqueryObject(this.$template) && this.options.autoDraw) {
                    //当前form无数据，有模板，且为 autoDraw， 则画一个子 form
                    this.$forms = $(_config_.selector.default);
                    this.$el.empty();
                    this.addForm();
                } else {
                    //if ($.com.isJqueryObject(this.$forms) && !$.com.isJqueryObject(this.$template))
                    //当前form无数据，无模板此时不判断autoDraw，此时认为子form已存在，则取子form初始化
                    this.$forms.each(function () {
                        var $this = $(this);
                        var data;
                        if ($this.is(_config_.selector.formArray)) {
                            data = [];
                        } else {
                            data = {};
                        }

                        that.options.data.push(data);
                        if ($.com.hasValue($this.data(_config_.form.namespace))) {
                            $this.form('refresh', data, force);
                        } else {
                            $this.form({ data: data });
                        }
                    });
                }
            } else {
                //当前form有数据， 则为编辑， 如有removable==true的数据，需预先排序让这种数据排到最后边
                var formData = this.options.data.filter(function (x) { return x.removable !== true; });
                if ($.com.isJqueryObject(this.$template)) {
                    //当前form有数据，有模板，按数据画子 form
                    this.$forms = $(_config_.selector.default);
                    this.$el.empty();
                    for (var i = 0; i < formData.length; i++) {
                        this.addForm(formData[i]);
                    }
                } else {
                    //有数据，无模板，按已存在的子form按数据画出界面
                    for (var i = 0; i < this.$forms.length; i++) {
                        var $this = this.$forms.eq(i);
                        var data = formData[i];
                        if ($.com.hasValue($this.data(_config_.form.namespace))) {
                            $this.form('refresh', data, force);
                        } else {
                            $this.form({ data: data });
                        }
                    }
                }
            }
        } else {
            //当前form为对象, 子级form取值取 data[field]
            this.$forms.each(function () {
                var $this = $(this);
                var field = $this.attr(_config_.attribute.input);
                if (!$.com.hasValue(that.options.data[field])) {
                    if ($this.is(_config_.selector.formArray)) {
                        that.options.data[field] = [];
                    } else {
                        that.options.data[field] = {};
                    }
                }

                var data = that.options.data[field];
                if (!data.removable) {
                    if ($.com.hasValue($this.data(_config_.form.namespace))) {
                        $this.form('refresh', data, force);
                    } else {
                        $this.form({ data: data });
                    }
                }
            });
        }
    };

    //使用模板为当前form添加一个子form, 并自动刷新相关数据
    Form.prototype.addForm = function (data) {
        if ($.com.isJqueryObject(this.$template)) {
            var $template = this.$template.clone();
            if (this.options.drawPlacement === 'before') {
                this.options.drawPlacement = 'prepend';
            }
            if (this.options.drawPlacement === 'after') {
                this.options.drawPlacement = 'append';
            }

            if ($.isFunction(this.$el[this.options.drawPlacement])) {
                if ($.com.hasValue(data)) {
                    var removable = data.removable || ((data.DataStatus || '').toLowerCase() === 'deleted' ? false : undefined);
                    if ($.com.hasValue(removable)) {
                        this.$el.attr('data-removable', removable);
                    }
                }

                var $availableForm = this.$forms.not('[data-removable]');
                var $removableForm = this.$forms.filter('[data-removable]');
                if ($.com.isJqueryObject($removableForm) && $.com.hasValue(removable)) {
                    if (this.options.drawPlacement === 'append') {
                        $removableForm.last().after($template);
                    } else {
                        $removableForm.first().before($template);
                    }
                } else if ($.com.isJqueryObject($availableForm)) {
                    if (this.options.drawPlacement === 'append') {
                        $availableForm.last().after($template);
                    } else {
                        $availableForm.first().before($template);
                    }
                } else {
                    this.$el[this.options.drawPlacement]($template);
                }

                var $form = $template;
                if (!$form.is(_config_.selector.form)) {
                    $form = $template.find(_config_.selector.form).first();
                }

                if ($form.hasValue()) {
                    if (!$.com.hasValue(data)) {
                        if ($form.is(_config_.selector.formArray)) {
                            data = [];
                        } else {
                            data = {};
                        }

                        this.options.data.push(data);
                    } else {
                        if ($.com.isArray(this.options.data)) {
                            if (!this.options.data.exist(function (x) { return x.serial === data.serial; })) {
                                this.options.data.push(data);
                            }
                        } else {
                            this.options.data.push(data);
                        }
                    }

                    $form.form({ data: data });
                    if (this.options.drawPlacement === 'append') {
                        this.$forms.push($form[0]);
                    } else {
                        this.$forms.splice(0, 0, $form[0]);
                    }

                    if (this.options.autoRefreshLayout) {
                        this.refreshLayout();
                    }
                }
            }
        }
    };

    //移除当前form, 并自动刷新相关数据
    Form.prototype.removeForm = function (removable) {
        if ($.com.isJqueryObject(this.$form)) {
            var form = this.$form.data(_config_.form.namespace);
            if ($.com.hasValue(this.options.field) && $.com.hasValue(form) && $.com.hasValue(form.options) && $.com.hasValue(form.options.data)) {
                var data = this.options.data;
                data.removable = removable;
                if (form.options.type === _config_.default.arrayTypeName && $.com.isArray(form.options.data)) {
                    form.options.data.remove(this.options.data, 'serial');
                } else {
                    delete form.options.data[this.options.field];
                }

                this.destroy();

                if (!$.com.isNullOrEmpty(removable)) {
                    data.DataStatus = 'deleted';
                    if (form.options.type === _config_.default.arrayTypeName && $.com.isArray(form.options.data)) {
                        form.options.data.push(data);
                    } else {
                        form.options.data[this.options.field] = data;
                    }

                    if (!removable) {
                        //不删除元素也不删除数据， 删除后的元素显示到底部
                        form.addForm(data);
                    }
                }

                if (form.options.autoRefreshLayout) {
                    form.refreshLayout();
                }
            }
        } else {
            this.destroy();
        }
    };

    Form.prototype.writeBack = function () {
        this.$inputs.input('writeBack');
        if (this.$forms.hasValue()) {
            this.$forms.form('writeBack');
        }
    };

    Form.prototype.getFormData = function () {
        this.writeBack();

        //var data = $.extend(true, $.isArray(this.options.data) ? [] : {}, this.options.data);
        return $.com.cleanData(this.options.data);
    };

    Form.prototype.getQueryExpression = function () {
        if ($.com.isJqueryObject(this.$inputs)) {
            this.options.dataSource = {};
            var filters = [];
            var queryString = {};

            $.each(this.$inputs, function () {
                var data = $(this).input('getData');
                if ($.com.hasValue(data)) {
                    var value = data.getQueryExpression();
                    if (!$.com.isNullOrEmpty(value)) {
                        if ($.com.hasValue(data.options.logic) && $.com.hasValue(data.options.operator) && data.options.logic && data.options.operator) {
                            filters = filters.concat(value);
                        } else {
                            queryString = $.extend(queryString, value);
                        }
                    }
                }
            });

            if ($.com.isArray(filters)) {
                this.options.dataSource.filter = {
                    logic: this.options.logic,
                    filters: filters
                };
            }
            if (!$.com.isNullOrEmpty(queryString)) {
                this.options.dataSource.transport = {
                    read: { data: queryString }
                };
            }
        }
    };

    Form.prototype.destroy = function () {
        var formData = this.$form.data(_config_.form.namespace);
        if ($.com.hasValue(formData) && formData.$forms.hasValue()) {
            formData.$forms.removeItem(this.$el);
        }

        while ($.com.isJqueryObject(this.$inputs)) {
            this.$inputs.eq(0).input('destroy');
        }

        while ($.com.isJqueryObject(this.$forms)) {
            this.$forms.eq(0).form('destroy');
        }

        while ($.com.isJqueryObject(this.$buttons)) {
            this.$buttons.eq(0).button('destroy');
        }

        this.$el.removeData(_config_.form.namespace);
        this.$el.remove();
    };

    Form.prototype.validate = function (options) {
        var $invalid;
        var isValid = options
        this.options.valid = true;
        if (options !== '*' && options !== false) {
            options = (this.options.validator || []).concat(options || []);
        }

        options = options || [];

        if ($.isArray(options) || options === '*') {
            var that = this;
            if (this.options.type === _config_.default.arrayTypeName) {
                if (this.$forms.hasValue()) {
                    for (var i = this.$forms.length - 1; i >= 0; i--) {
                        var $this = this.$forms.eq(i);
                        var valid = true;
                        var validator = options;
                        valid = $this.form('validate', validator);
                        this.options.valid = this.options.valid && valid;
                    }
                }
            } else {
                for (var i = this.$forms.length - 1; i >= 0; i--) {
                    var $this = this.$forms.eq(i);
                    var valid = true;
                    var validator = options;

                    if (options !== '*') {
                        var field = $this.attr(_config_.attribute.input);
                        validator = options.first(function (x) {
                            return x.field === field;
                        });
                        if ($.com.hasValue(validator)) {
                            validator = validator['validator'];
                        } else {
                            validator = false;
                        }
                    }

                    valid = $this.form('validate', validator);
                    this.options.valid = this.options.valid && valid;
                }

                this.$inputs.each(function () {
                    var $this = $(this);
                    var valid = true;
                    //此处只能给undefined, 因为要和input自己的条件合并, 传递false则会取消验证
                    var validator = undefined;

                    if (options !== '*') {
                        var field = $this.attr(_config_.attribute.input);
                        validator = options.first(function (x) {
                            return x === field || x.field === field;
                        });
                        if ($.com.hasValue(validator) && !that.options.validateWhenDisplay) {
                            validator = validator['validator'];
                        } else {
                            validator = false;
                        }
                    }

                    valid = $this.input('validate', validator);
                    that.options.valid = that.options.valid && valid;
                    if (!$.com.isJqueryObject($invalid) && !valid) {
                        $invalid = $this;
                    }
                });
            }
        }

        if ($.isFunction(this.options.validate) && isValid !== false) {
            this.options.valid = this.options.valid && this.execute(this.options.validate);
        }

        if ($.com.isJqueryObject($invalid) && !this.options.valid) {
            $.com.switchTabs($invalid);
            $invalid.focus();
        }

        return this.options.valid;
    };

    // plug-in
    $.fn.form = function (options) {
        var args = Array.prototype.slice.call(arguments, 1);
        if (args.length === 0) {
            args = undefined;
        }
        var value = undefined;
        this.each(function () {
            var $this = $(this);
            var data = $this.data(_config_.form.namespace);
            // initial if nover
            if ($.com.isNullOrEmpty(data)) {
                data = new Form(this, typeof options === 'object' && options);
                $this.data(_config_.form.namespace, data);
            }

            if (typeof options === 'string') {
                //if options is not a method
                if ($.inArray(options, data.methods) < 0) {
                    throw 'Unknown method: ' + options;
                }

                //if options is a plugin method, to execute
                value = data[options].apply(data, args);
            }
        });

        return typeof value === 'undefined' ? this : value;
    };
})(jQuery);